// Copyright (c) 2020 cgsdfc
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#pragma once

#include <functional>  // std::hash

namespace pyast {

class Arena;

// This is the public base class for all ASTs.
// Every AST node represents a raw pointer to the underlying python's ast struct
// augmented with a pointer to Arena, which is used to implement some automatic
// management.
// However, AST classes do not impose any ownership semantic on its Arena
// member. This is to maximium efficiency and encourage the client of ASTs to
// manage the lifetime of ASTs and Arena explicitly. This also gives client
// freedom to choose different ownership semantic as they wish.
class AstNode {
 public:
  AstNode() = default;

  Arena* GetArena() const { return arena_; }

  explicit operator bool() const { return !!ptr_; }

  uintptr_t GetId() const {
    return reinterpret_cast<uintptr_t>(GetUntypedRawPtr());
  }

 protected:
  void* GetUntypedRawPtr() const { return ptr_; }

  AstNode(void* ptr, Arena* arena) : arena_(arena), ptr_(ptr) {}

  // These two members's nullness are the same at a time.
  Arena* arena_ = nullptr;
  void* ptr_ = nullptr;
};

inline bool operator==(const AstNode& lhs, const AstNode& rhs) {
  return lhs.GetId() == rhs.GetId();
}

inline bool operator<(const AstNode& lhs, const AstNode& rhs) {
  return lhs.GetId() < rhs.GetId();
}

}  // namespace pyast

// Make AstNode hashable.
namespace std {
template <>
struct hash<pyast::AstNode> {
  size_t operator()(const pyast::AstNode& a) const noexcept {
    return reinterpret_cast<size_t>(a.GetId());
  }
};
}  // namespace std
